#include<iostream>
#include<string>
#include"until.hpp"
#include"mongoose.h"
#include"session.hpp"
#include<unordered_map>

namespace im
{
    class Server
    {
    private:
        SessionManager* _ss_manager;
        UserTable* _user_table;
        MsgTable* _msg_table;
        struct mg_mgr _mgr; //服务器句柄
        std::string _rootdir = "./wwwroot";
        //将连接和session_id关联起来
        std::unordered_map<struct mg_connection*,uint64_t> _conn_ss;
    private:
        static bool user_add(struct mg_connection* c,struct mg_http_message* hm,Server* srv)
        {
            //1.解析出正文数据
            std::string req_body(hm->body.ptr,hm->body.len);
            Json::Value user_json;
            Json::Value resp_json;
            bool ret = JsonUntil::unserialize(req_body,&user_json);
            if(ret==false)
            {
                resp_json["result"] = false;
                resp_json["reason"] = "用户信息解析失败";
                std::string body;
                JsonUntil::serialize(resp_json,&body);
                mg_http_reply(c,400,NULL,"%s",body.c_str());
                return false;
            }
            //2.将用户数据添加到用户信息表中
            UserTable* table = srv->get_user_table();
            ret = table->Insert(user_json);
            if(ret==false)
            {
                resp_json["result"] = false;
                resp_json["reason"] = "向数据库插入信息出错";
                std::string body;
                JsonUntil::serialize(resp_json,&body);
                mg_http_reply(c,500,NULL,"%s",body.c_str());
                return false;
            }
            //3.添加成功返回200成功响应
            resp_json["result"] = true;
            resp_json["reason"] = "新增用户成功";
            std::string body;
            JsonUntil::serialize(resp_json,&body);
            mg_http_reply(c,200,NULL,"%s",body.c_str());
        }
        static bool user_login(struct mg_connection* c,struct mg_http_message* hm,Server* srv)
        {
            //1.解析出正文数据
            std::string req_body(hm->body.ptr,hm->body.len);
            Json::Value user_json;
            Json::Value resp_json;
            //2.进行Json反序列化
            bool ret = JsonUntil::unserialize(req_body,&user_json);
            if(ret==false)
            {
                resp_json["result"] = false;
                resp_json["reason"] = "用户信息解析失败";
                std::string body;
                JsonUntil::serialize(resp_json,&body);
                mg_http_reply(c,400,NULL,"%s",body.c_str());
                return false;
            }
            //3.从数据库中获取验证
            UserTable* table = srv->get_user_table();
            ret = table->check_login(user_json);
            if(ret==false)
            {
                resp_json["result"] = false;
                resp_json["reason"] = "用户名密码错误";
                std::string body;
                JsonUntil::serialize(resp_json,&body);
                mg_http_reply(c,401,NULL,"%s",body.c_str());
                return false;
            }
            //4.登录验证成功，为客户端新建会话
            SessionManager* ssm=srv->get_ss_manager();
            uint64_t ssid = ssm->insert(user_json);
            //将session_id与连接关联起来
            std::unordered_map<struct mg_connection*,uint64_t>* conn_ss=srv->get_conn_ss();
            (*conn_ss)[c]=ssid;
            //Set-Cookie: SSID=1 头部字段
            std::string cookie = "Set-Cookie: SSID="+std::to_string(ssid)+"\r\n";

            //5.验证成功返回200
            resp_json["result"] = true;
            resp_json["reason"] = "用户登录成功";
            std::string body;
            JsonUntil::serialize(resp_json,&body);
            mg_http_reply(c,200,cookie.c_str(),"%s",body.c_str());
        }
        static bool get_history_msg(struct mg_connection* c,struct mg_http_message* hm,Server* srv)
        {
            MsgTable* table=srv->get_msg_table();
            Json::Value msgs;
            bool ret = table->select_part(6000,&msgs);
            if(ret==false)
            {
                Json::Value resp_json;
                resp_json["result"] = false;
                resp_json["reason"] = "获取历史聊天记录失败";
                std::string body;
                JsonUntil::serialize(resp_json,&body);
                mg_http_reply(c,400,NULL,"%s",body.c_str());
                return false;
            }
            std::string body;
            JsonUntil::serialize(msgs,&body);
            mg_http_reply(c,200,NULL,"%s",body.c_str());
        }
        //广播信息
        static void broadcast_msg(struct mg_mgr* mgr,const std::string& msg)
        {
            //struct mg_connection* 链表---保存了所有连接
            struct mg_connection* conn = mgr->conns;
            for(;conn!=NULL;conn=conn->next)
            {
                //如果是web连接
                if(conn->is_websocket)
                {
                    //将数据进行发送
                    mg_ws_send(conn,msg.c_str(),msg.size(),WEBSOCKET_OP_TEXT);
                }
            }
        }
        static void handler(struct mg_connection* c,int ev,void* ev_data,void* fn_data)
        {
            Server* srv = (Server*)fn_data;
            //如果是http请求
            if(ev==MG_EV_HTTP_MSG)
            {
                struct mg_http_message* hm = (struct mg_http_message*)ev_data;
                //获取请求方法和资源路径
                std::string method(hm->method.ptr,hm->method.len);
                std::string uri(hm->uri.ptr,hm->uri.len);
                if(method == "POST" && uri == "/user")
                {
                   //新增用户请求
                   user_add(c,hm,srv);
                }
                else if(method == "POST" && uri == "/login")
                {
                    //登录验证
                    user_login(c,hm,srv);
                }
                else if(method == "PUT" && uri == "/user/password")
                {
                    //修改密码
                }
                else if(method == "PUT" && uri == "/user/nickname")
                {
                    //修改昵称
                }
                else if(method == "DELETE" && uri == "/user")
                {
                    //删除用户
                }
                else if(method == "GET" && uri == "/user")
                {
                    //获取用户信息
                }
                else if(method == "GET" && uri == "/message")
                {
                    //获取历史消息
                    get_history_msg(c,hm,srv);
                }
                else if(method == "GET" && uri == "/ws")
                {
                    //协议切换请求-切换到websocket协议
                    //获取cookie信息
                    struct mg_str* cookie=mg_http_get_header(hm,"Cookie");
                    if(cookie==NULL)
                    {
                        //没有获取到，返回401
                        mg_http_reply(c,401,"","%s","401 Unauthorized");
                        return;
                    }
                    //获取到cookie信息,例如: SSID=1 
                    std::string cookie_str(cookie->ptr,cookie->len);
                    size_t pos = cookie_str.find("=");
                    //截取=后面的信息
                    std::string ssid_str = cookie_str.substr(pos+1);
                    //获取到cookie信息中的id
                    uint64_t ssid = std::stoi(ssid_str);
                    SessionManager* ssm = srv->get_ss_manager();
                    //判断获取到的id信息是否存在，不存在则返回401
                    if(ssm->exists(ssid)==false)
                    {
                        mg_http_reply(c,401,"","%s","401 Unauthorized");
                        return;
                    }
                    mg_ws_upgrade(c,hm,NULL);
                }
                //静态资源请求
                else
                {
                    struct mg_http_serve_opts opts = {.root_dir=srv->rootdir().c_str()};
                    mg_http_serve_dir(c,hm,&opts);
                }
            }
            else if(ev==MG_EV_WS_OPEN)
            {
                //WEBSOCKET握手成功
                //1.获取连接对应的session_id
                std::unordered_map<struct mg_connection*,uint64_t>* conn_ss=srv->get_conn_ss();
                auto it=conn_ss->find(c);
                if(it==conn_ss->end())
                {
                    return;
                }
                //2.广播用户上线
                uint64_t ssid = it->second;
                SessionManager* ssm = srv->get_ss_manager();
                
                bool ret = ssm->exists(it->second);
                //找到session_id对应的用户信息，广播这个用户下线
                if(ret!=false)
                {
                    Json::Value user;
                    ssm->get_session_user(ssid,&user);
                    std::string msg=user["nickname"].asString()+"上线了!";
                    broadcast_msg(srv->get_mgr(),msg);
                }
            }
            else if(ev==MG_EV_WS_MSG)
            {
                //WEBSOCKRT，数据通信，客户端发送了一条聊天信息,广播这条信息
                struct mg_ws_message* wsmsg=(struct mg_ws_message*)ev_data;
                std::string msg(wsmsg->data.ptr,wsmsg->data.len);
                
                broadcast_msg(srv->get_mgr(),msg);
            }
            else if(ev==MG_EV_CLOSE)
            {
                //通信连接断开
                //1.获取连接对应的session_id
                std::unordered_map<struct mg_connection*,uint64_t>* conn_ss=srv->get_conn_ss();
                auto it=conn_ss->find(c);
                if(it==conn_ss->end())
                {
                    return;
                }
                //2.移除session
                uint64_t ssid = it->second;
                SessionManager* ssm = srv->get_ss_manager();
                
                bool ret = ssm->exists(it->second);
                //找到session_id对应的用户信息，广播这个用户下线
                if(ret!=false)
                {
                    Json::Value user;
                    ssm->get_session_user(ssid,&user);
                    std::string msg=user["nickname"].asString()+"下线了!";
                    broadcast_msg(srv->get_mgr(),msg);
                    ssm->remove(it->second);
                }
                
                //3.将与该session_id对应的连接从关系表中移除
                conn_ss->erase(c);
            }
            
        }
    public:
        //对句柄进行初始化
        Server(const std::string& host,const std::string& user,
      const std::string& pass,const std::string& db,int port)
        {
            _user_table = new UserTable(host,user,pass,db,port);
            _msg_table = new MsgTable(host,user,pass,db,port);
            _ss_manager = new SessionManager();
            mg_mgr_init(&_mgr);
        }
        struct mg_mgr* get_mgr()
        {
            return &_mgr;
        }
        const std::string& rootdir()
        {
            return _rootdir;
        }
        UserTable* get_user_table()
        {
            return _user_table;
        }
        MsgTable* get_msg_table()
        {
            return _msg_table;
        }
        SessionManager* get_ss_manager()
        {
            return _ss_manager;
        }
        std::unordered_map<struct mg_connection*,uint64_t>* get_conn_ss()
        {
            return &_conn_ss;
        }
        void Start(int port)
        {
            std::string address = "0.0.0.0:"+std::to_string(port);
            //绑定地址，设置回调
            mg_http_listen(&_mgr,address.c_str(),handler,this);
            //开始循环接收所有描述符（是否可读）
            for(;;)
            {
                mg_mgr_poll(&_mgr,1000);
            }
        }
    };
}